package com.hj.springframework2.aop;

import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.scaffold.subclass.ConstructorStrategy;
import net.bytebuddy.implementation.InvocationHandlerAdapter;
import net.bytebuddy.matcher.ElementMatchers;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * @Author : hujing
 * @Date: 2025/6/24 9:55
 * @Description:
 */
public class ProxyResolver {
    
    private final Logger logger = LoggerFactory.getLogger(getClass());
    
    private final ByteBuddy byteBuddy = new ByteBuddy();
    
    private static ProxyResolver INSTANCE;
    
    public static ProxyResolver getInstance(){
        if (INSTANCE == null){
            INSTANCE = new ProxyResolver();
        }
        return INSTANCE;
    }

    /**
     * 产生代理对象
     * @param bean
     * @param handler
     * @return
     * @param <T>
     */
    public <T> T createProxy(T bean, InvocationHandler handler){
        Class<?> targetClass = bean.getClass();
        logger.info("create proxy for bean {} @{}", targetClass.getName(), Integer.toHexString(bean.hashCode()));

        // 生成代理类对象
        Class<?> proxyClass = this.byteBuddy
                // 使用无参构造函数创建子类
                .subclass(targetClass, ConstructorStrategy.Default.DEFAULT_CONSTRUCTOR)
                // 拦截所有公共方法
                .method(ElementMatchers.isPublic())
                .intercept(InvocationHandlerAdapter.of(new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        // 调用目标方法
                        return handler.invoke(bean, method, args);
                    }
                }))
                .make()
                .load(targetClass.getClassLoader())
                .getLoaded();
        
        // 生成代理实例
        Object proxy;
        try {
            proxy = proxyClass.getConstructor().newInstance();
        } catch (Exception e){
            throw new RuntimeException(e);
        }
        
        return (T) proxy;
    }
}
